﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RpcGenerator.CSharp;

namespace RpcGenerator
{
    class MessageGenerator
    {
        public void Generate(MessageDescription description, IMessageSerializer serializer, MessageFormatType format)
        {
            switch (format)
            {
                case MessageFormatType.Protobuf:
                    {
                        MessageProtobufGenerator pgenerator = new MessageProtobufGenerator();
                        pgenerator.Generate(description, serializer);
                        break;
                    }
                case MessageFormatType.Json:
                    {
                        MessageJsonGenerator jgenerator = new MessageJsonGenerator();
                        jgenerator.Generate(description, serializer);
                        break;
                    }
                default:
                    {
                        BeginMessage(description, serializer);

                        WriteMessageId(description, serializer);
                        WriteFields(description, serializer);

                        BeginReadFunction(description, serializer);
                        EndReadFunction(serializer);

                        BeginWriteFunction(description, serializer);
                        EndWriteFunction(serializer);

                        EndMessage(serializer);
                        break;
                    }
            }
        }

        private void BeginMessage(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine(string.Format("class {0} : IMessage", description.Name));
            serializer.BeginBlock();
        }

        private void EndMessage(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void WriteMessageId(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine("public Int32 MessageId { get{ return ");
            serializer.Apppend(description.MessageId.ToString());
            serializer.Apppend(";}}");

            serializer.NewLine("public UInt16  Flags     { get;set;}");
            serializer.NewLine("public Int32   Sessionid { get;set;}");
            serializer.NewLine("public Int32   OpCode    { get;set;}");
        }

        private void WriteFields(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            foreach (MessageFieldDescription mfd in description.Fields)
            {
                if (mfd.IsGenericType())
                {
                    serializer.NewLine(string.Format("public {0} {1};", mfd.GenericTypeName, mfd.Name));
                }
                else
                {
                    serializer.NewLine(string.Format("public {0} {1};", mfd.Type.Name, mfd.Name));
                }
            }
        }

        private void BeginWriteFunction(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine("public void Write(NetStream ns)");
            serializer.BeginBlock();

            WriteObjectMask(description, serializer);

            foreach (MessageFieldDescription mfd in description.Fields)
            {
                WriteField(mfd, serializer);
            }
        }

        private void WriteObjectMask(MessageDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int objMaskCount = description.ObjectCount / description.ObjectMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("byte objectmask{0} = 0;", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("UInt16 objectmask{0} = 0;", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("UInt32 objectmask{0} = 0;", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("UInt64 objectmask{0} = 0;", i));
                                break;
                            }
                    }
                }

                foreach (MessageFieldDescription mfd in description.Fields)
                {
                    if (!mfd.IsPrimitiveType())
                    {
                        if (mfd.IsString())
                        {
                            serializer.NewLine(string.Format("if(string.IsNullOrEmpty({0}))", mfd.Name));
                        }
                        else
                        {
                            serializer.NewLine(string.Format("if(null=={0})", mfd.Name));
                        }
                        
                        serializer.BeginBlock();
                        int maskIndex = (mfd.ObjectIndex - 1) / description.ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % description.ObjectMaskSize;
                        serializer.NewLine(string.Format("objectmask{0} |= {1};", maskIndex, 1 << maskInnerIndex));
                        serializer.EndBlock();
                    }
                }

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("ns.WriteByte(objectmask{0});", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt16(objectmask{0});", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt32(objectmask{0});", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt64(objectmask{0});", i));
                                break;
                            }
                    }
                }
            }
        }

        private void WriteField(MessageFieldDescription mfd, IMessageSerializer serializer)
        {
            switch (mfd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        WritePrimitiveType(mfd.Type, mfd.Name, serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        serializer.NewLine(string.Format("if({0}!=null)", mfd.Name));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("UInt16 count = (UInt16){0}.Count;", mfd.Name));
                            WritePrimitiveType(typeof(UInt16), "count", serializer);
                            serializer.NewLine("for(UInt16 i = 0;i<count;++i)");
                            serializer.BeginBlock();
                                Type genericType = mfd.GetGenericType();
                                switch (MessageFieldDescription.GetMessageFieldType(genericType))
                                {
                                    case MessageFieldType.PrimitiveType:
                                        {
                                            WritePrimitiveType(genericType, string.Format("{0}[i]", mfd.Name), serializer);
                                            break;
                                        }
                                    case MessageFieldType.ClientType:
                                        {
                                            serializer.NewLine(string.Format("{0}[i].Write(ns);", mfd.Name));
                                            break;
                                        }
                                    case MessageFieldType.GenericType:
                                        {
                                            serializer.NewLine("模板中不支持模板");
                                            break;
                                        }
                                }
                            serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        serializer.NewLine(string.Format("if(null!={0})", mfd.Name));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0}.Write(ns);", mfd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private void WritePrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("ns.WriteInt8({0});", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("ns.WriteUInt8({0});", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("ns.WriteInt16({0});", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("ns.WriteUInt16({0});", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("ns.WriteUInt32({0});", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("ns.WriteInt64({0});", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("ns.WriteUInt64({0});", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("ns.WriteFloat({0});", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("ns.WriteFloat((float){0});", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("ns.WriteBool({0});", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("ns.WriteInt32((Int32){0});", name));
                return;
            }

            if (tp == typeof(DateTime))
            {
                serializer.NewLine(string.Format("ns.WriteString({0}.ToString());", name));
            }

            if (tp == typeof(string))
            {
                serializer.NewLine(string.Format("ns.WriteString({0});", name));
            }
        }

        private void EndWriteFunction(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void BeginReadFunction(MessageDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
              serializer.NewLine("public void Read(NetStream ns)");
            serializer.BeginBlock();

            ReadObjectMask(description, serializer);

            foreach (MessageFieldDescription mfd in description.Fields)
            {
                ReadField(mfd, serializer, description);
            }
        }

        private static void ReadObjectMask(MessageDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int objMaskCount = description.ObjectCount / description.ObjectMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.ObjectMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("byte objectmask{0} = ns.ReadByte();", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("UInt16 objectmask{0} = ns.ReadUInt16();", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("UInt32 objectmask{0} = ns.ReadUInt32();", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("UInt64 objectmask{0} = ns.ReadUInt64();", i));
                                break;
                            }
                    }
                }
            }
        }

        private void ReadField(MessageFieldDescription mfd, IMessageSerializer serializer, MessageDescription description)
        {
            switch (mfd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        ReadPrimitiveType(mfd.Type, mfd.Name, serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        int maskIndex = (mfd.ObjectIndex - 1) / description.ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % description.ObjectMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0} = new {1}();", mfd.Name, mfd.GenericTypeName));
                            serializer.NewLine("UInt16 count = ns.ReadUInt16();");
                            serializer.NewLine("for(UInt16 i = 0;i<count;++i)");
                            serializer.BeginBlock();
                                Type genericType = mfd.GetGenericType();
                                switch (MessageFieldDescription.GetMessageFieldType(genericType))
                                {
                                    case MessageFieldType.PrimitiveType:
                                        {
                                            serializer.NewLine(string.Format("{0} member;", genericType.Name));
                                            ReadPrimitiveType(genericType, "member", serializer);
                                            serializer.NewLine(string.Format("{0}.Add(member);", mfd.Name));
                                            break;
                                        }
                                    case MessageFieldType.ClientType:
                                        {
                                            serializer.NewLine(string.Format("{0} member = new {0}();", genericType.Name));
                                            serializer.NewLine("member.Read(ns);");
                                            serializer.NewLine(string.Format("{0}.Add(member);", mfd.Name));
                                            break;
                                        }
                                    case MessageFieldType.GenericType:
                                        {
                                            serializer.NewLine("模板中不支持模板");
                                            break;
                                        }
                                }
                            serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        int maskIndex = (mfd.ObjectIndex - 1) / description.ObjectMaskSize;
                        int maskInnerIndex = (mfd.ObjectIndex - 1) % description.ObjectMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0} = new {1}();", mfd.Name, mfd.Type.Name));
                            serializer.NewLine(string.Format("{0}.Read(ns);", mfd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private void ReadPrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt8();", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt8();", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt16();", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt16();", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteUInt32();", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteInt64();", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt64();", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadBool();", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("{0} = ({1})ns.ReadInt32();", name, tp.Name));
                return;
            }

            if (tp == typeof(DateTime))
            {
                serializer.NewLine(string.Format("DateTime.TryParse(ns.ReadString(),out {0});", name));
            }

            if (tp == typeof(string))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadString();", name));
            }
        }

        private void EndReadFunction(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }
    }
}
